/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifdef __linux__
#include <sys/statvfs.h>
#include <sys/vfs.h>
#else
#include <sys/param.h>
#include <sys/mount.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>

#include <debug.h>

#include "fcalls.h"
#include "main.h"
#include "util.h"

void FsTest (void)
{
#ifdef __linux__
	struct statfs s1;
	struct statfs s2;
	struct statvfs v1;
	struct statvfs v2;
	int fd = open(".", O_RDONLY, 0);
	statfs(".", &s1);
	fstatfs(fd, &s2);
	statvfs(".", &v1);
	fstatvfs(fd, &v2);
	if (Local_option.flaky) {
		/* Can't be sure file system values won't change between calls */
		CheckEq(&s1, &s2, sizeof(s1));
		CheckEq(&v1, &v2, sizeof(v1));
		Check(s1.f_bsize   == v1.f_bsize);
		Check(s1.f_blocks  == v1.f_blocks);
		Check(s1.f_bfree   == v1.f_bfree);
		Check(s1.f_bavail  == v1.f_bavail);
		Check(s1.f_files   == v1.f_files);
		Check(s1.f_ffree   == v1.f_ffree);
		Check(s1.f_namelen == v1.f_namemax);
	}
	close(fd);
#endif
}

void StatTest (void)
{
	enum {	FILE_SZ    = 1759,
		FILE_TRUNC = 17,
		FILE_GROW  = 10937,
		USER_A     = 12345,
		GROUP_A    = 23456,
		USER_B     = 65431,
		GROUP_B    = 76543 };
	struct stat s1;
	struct stat s2;
	char *name = RndName();
	CrFile(name, FILE_SZ);
	stat(name, &s1);
	int fd = open(name, O_RDONLY, 0);
	fstat(fd, &s2);
	CheckEq(&s1, &s2, sizeof(s1));
	Check(s1.st_size == FILE_SZ);
	close(fd);

	if (Local_option.is_root) {
		chown(name, USER_A, GROUP_A);
		stat(name, &s1);
		Check(s1.st_uid == USER_A);
		Check(s1.st_gid == GROUP_A);

		fd = open(name, O_RDWR, 0);
		fchown(fd, USER_B, GROUP_B);
		fstat(fd, &s1);
		Check(s1.st_uid == USER_B);
		Check(s1.st_gid == GROUP_B);
		close(fd);
	}
	/* Truncate can both shrink and grow a file */
	truncate(name, FILE_TRUNC);
	stat(name, &s1);
	Check(s1.st_size == FILE_TRUNC);
	truncate(name, FILE_GROW);
	stat(name, &s1);
	Check(s1.st_size == FILE_GROW);

	fd = open(name, O_RDWR, 0);
	ftruncate(fd, FILE_TRUNC);
	fstat(fd, &s1);
	Check(s1.st_size == FILE_TRUNC);
	ftruncate(fd, FILE_GROW);
	fstat(fd, &s1);
	Check(s1.st_size == FILE_GROW);
	close(fd);

	fd = open(name, O_RDONLY, 0);
	ftruncateErr(EINVAL, fd, FILE_TRUNC);
	fstat(fd, &s1);
	Check(s1.st_size == FILE_GROW);
	close(fd);

	/* Dup - fds share seek pointer */
	int fd1 = open(name, O_RDWR | O_TRUNC, 0);
	int fd2 = dup(fd1);
	char b[10];
	write(fd1, "abc", 3);
	lseek(fd1, 0, 0);
	read(fd1, b, 1);
	Check(b[0] == 'a');
	read(fd2, b, 1);
	Check(b[0] == 'b');
	close(fd1);
	close(fd2);
	fd2 = dupErr(EBADF, fd1);

	/* Dup2 - like dup but can pick fd number */
	/* TODO(taysom) add more tests */
	fd1 = open(name, O_RDWR | O_TRUNC, 0);
	fd2 = open(name, O_RDONLY, 0);
	fd2 = dup2(fd1, fd2);
	write(fd1, "abc", 3);
	lseek(fd1, 0, 0);
	read(fd1, b, 1);
	Check(b[0] == 'a');
	read(fd2, b, 1);
	Check(b[0] == 'b');
	close(fd1);
	close(fd2);

	/* Link */
	char *name2 = RndName();
	link(name, name2);
	stat(name, &s1);
	Check(s1.st_nlink == 2);
	unlink(name2);
	free(name2);
	stat(name, &s1);
	Check(s1.st_nlink == 1);
	/* TODO(taysom): need to try 64K links */

	unlink(name);
	free(name);
}
